namespace HybridCLR.Extension
{
    public class CacheFile
    {
        public System.Action<string, byte[]> FileWriteAllBytes = System.IO.File.WriteAllBytes;
        public System.Func<string, bool> FileExists = System.IO.File.Exists;
        public string RemoteCatalogUrl = "http://localhost";
        public string CacheCatalogUrl;
        private string InternalCacheCatalogUrl => !string.IsNullOrEmpty(CacheCatalogUrl) ? CacheCatalogUrl : System.IO.Path.Combine(UnityEngine.Application.persistentDataPath, "cache_catalog.json");

        /// <summary>
        /// 检查更新
        /// </summary>
        /// <returns></returns>
        public System.Collections.IEnumerator CheckUpdateEnumerator()
        {
            string serverCatalogUrl = RemoteCatalogUrl;
            string cacheCatalogPath = InternalCacheCatalogUrl;
            
            // 下载文件
            var headHandle = UnityEngine.Networking.UnityWebRequest.Head(serverCatalogUrl);
            yield return headHandle.SendWebRequest();
            if (headHandle.result == UnityEngine.Networking.UnityWebRequest.Result.Success)
            {
                var catalogHandle = UnityEngine.Networking.UnityWebRequest.Get(serverCatalogUrl);
                var requestCatalog = catalogHandle.SendWebRequest();
                yield return requestCatalog;
                if (catalogHandle.result == UnityEngine.Networking.UnityWebRequest.Result.Success)
                {
                    FileWriteAllBytes?.Invoke(cacheCatalogPath, catalogHandle.downloadHandler.data);
                }
            }
            
            if (System.IO.File.Exists(InternalCacheCatalogUrl))
            {
                // 初始化时加载本地 catalog
                var initializeHandle = UnityEngine.AddressableAssets.Addressables.InitializeAsync();
                yield return initializeHandle;
                System.Collections.Generic.List<UnityEngine.AddressableAssets.ResourceLocators.IResourceLocator>
                    localLocators =
                        new System.Collections.Generic.List<
                            UnityEngine.AddressableAssets.ResourceLocators.IResourceLocator>();
                foreach (var e in UnityEngine.AddressableAssets.Addressables.ResourceLocators)
                {
                    localLocators.Add(e);
                }

                for (int i = 0; i < localLocators.Count; i++)
                {
                    if (localLocators[i].LocatorId == "AddressablesMainContentCatalog")
                        UnityEngine.AddressableAssets.Addressables.RemoveResourceLocator(localLocators[i]);
                }

                var loadContentHandle = UnityEngine.AddressableAssets.Addressables.LoadContentCatalogAsync(InternalCacheCatalogUrl);
                yield return loadContentHandle;
            }
        }
        
        /// <summary>
        /// 检查更新
        /// </summary>
        public async System.Threading.Tasks.Task CheckUpdateTask()
        {
            string serverCatalogUrl = RemoteCatalogUrl;
            await DownloadFileTask(serverCatalogUrl, InternalCacheCatalogUrl, FileWriteAllBytes);
            if (FileExists(InternalCacheCatalogUrl))
            {
                System.Threading.Tasks.TaskCompletionSource<object> tcs = new System.Threading.Tasks.TaskCompletionSource<object>();
                // 初始化时加载本地 catalog
                var initializeHandle = UnityEngine.AddressableAssets.Addressables.InitializeAsync();
                initializeHandle.Completed += result =>
                {
                    tcs.SetResult(null);
                };
                await tcs.Task;
                System.Collections.Generic.List<UnityEngine.AddressableAssets.ResourceLocators.IResourceLocator>
                    localLocators =
                        new System.Collections.Generic.List<
                            UnityEngine.AddressableAssets.ResourceLocators.IResourceLocator>();
                foreach (var e in UnityEngine.AddressableAssets.Addressables.ResourceLocators)
                {
                    localLocators.Add(e);
                }

                // Addressables 热更新场景中移除旧的资源映射表，防止资源加载错误或冲突。
                for (int i = 0; i < localLocators.Count; i++)
                {
                    if (localLocators[i].LocatorId == "AddressablesMainContentCatalog")
                        UnityEngine.AddressableAssets.Addressables.RemoveResourceLocator(localLocators[i]);
                }

                System.Threading.Tasks.TaskCompletionSource<object> tcs2 = new System.Threading.Tasks.TaskCompletionSource<object>();
                var loadContentHandle = UnityEngine.AddressableAssets.Addressables.LoadContentCatalogAsync(InternalCacheCatalogUrl);
                loadContentHandle.Completed += result =>
                {
                    tcs2.SetResult(null);
                };
                await tcs2.Task;
            }
        }
        
        /// <summary>
        /// 下载并缓存资源
        /// </summary>
        /// <param name="remoteFileUrl"></param>
        /// <param name="cacheFilePath"></param>
        /// <param name="writeAllBytes"></param>
        private async System.Threading.Tasks.Task DownloadFileTask(string remoteFileUrl, string cacheFilePath,
            System.Action<string, byte[]> writeAllBytes)
        {
            if (string.IsNullOrEmpty(remoteFileUrl))
            {
                GLog.Warning("RemoteCatalogUrl is null or empty ");
                return;
            }
            System.Threading.Tasks.TaskCompletionSource<object> tcs = new System.Threading.Tasks.TaskCompletionSource<object>();
            var headHandle = UnityEngine.Networking.UnityWebRequest.Head(remoteFileUrl);
            headHandle.SendWebRequest().completed += result =>
            {
                tcs.SetResult(null);
            };
            await tcs.Task;
            if (headHandle.result == UnityEngine.Networking.UnityWebRequest.Result.Success)
            {
                System.Threading.Tasks.TaskCompletionSource<object> tcs2 = new System.Threading.Tasks.TaskCompletionSource<object>();
                var catalogHandle = UnityEngine.Networking.UnityWebRequest.Get(remoteFileUrl);
                catalogHandle.SendWebRequest().completed += result =>
                {
                    tcs2.SetResult(null);
                };
                await tcs2.Task;
                if (catalogHandle.result == UnityEngine.Networking.UnityWebRequest.Result.Success)
                {
                    writeAllBytes?.Invoke(cacheFilePath, catalogHandle.downloadHandler.data);
                }
            }
        }
    }
}